/*
*
* 版权所有 Copyleft(c) 2005
* 刘炯个人工作室 Sharp-Mark Personal Studio
*
* 允许自由使用，复制，修改，传播和贩卖此源程序及其文档，而不必支付任何费用。
* 以上的版权信息必须出现在每一份复制品中。同时这份版权信息和许可声明
* 也必须出现在支持文档中。刘炯个人工作室不对此软件适于那种具体
* 用途做出任何阐述。此代码只作为代码本身提供对其品质不做任何保证。
*
*/

#ifndef _IMAGE_H_
#define _IMAGE_H_

#include <assert.h>
#include "vectors.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

// ====================================================================
// ====================================================================
// Simple image class

class Image {

public:

  // ========================
  // CONSTRUCTOR & DESTRUCTOR
  Image(int w, int h) {
    width = w;
    height = h;
    data = new Vec3f[width*height]; }
  ~Image() {
    delete [] data; }

  // =========
  // ACCESSORS
  int Width() const { return width; }
  int Height() const { return height; }
  const Vec3f& GetPixel(int x, int y) const {
    assert(x >= 0 && x < width);
    assert(y >= 0 && y < height);
    return data[y*width + x]; }
  
  // =========
  // MODIFIERS
  void SetAllPixels(const Vec3f &color) {
    for (int i = 0; i < width*height; i++) {
      data[i] = color; } }
  void SetPixel(int x, int y, const Vec3f &color) {
    assert(x >= 0 && x < width);
    assert(y >= 0 && y < height);
    data[y*width + x] = color; }

  // ===========
  // LOAD & SAVE
  static Image* LoadPPM(const char *filename);
  void SavePPM(const char *filename) const; 
  static Image* LoadTGA(const char *filename);
  void SaveTGA(const char *filename) const; 

private:

  // ==============
  // REPRESENTATION
  int width;
  int height;
  Vec3f *data;

};

// ====================================================================
// ====================================================================

// ====================================================================
// ====================================================================
// some helper functions for save & load

unsigned char ReadByte(FILE *file) {  
	unsigned char b;
	int success = fread((void*)&b,sizeof(unsigned char),1,file);
	assert (success == 1);
	return b;
}

void WriteByte(FILE *file, unsigned char b) {
	int success = fwrite((void*)&b,sizeof(unsigned char),1,file);
	assert (success == 1);
}

unsigned char ClampColorComponent(float c) {
	int tmp = int (c*255);
	if (tmp < 0) tmp = 0;
	if (tmp > 255) tmp = 255;
	return (unsigned char)tmp;
}

// ====================================================================
// ====================================================================
// Save and Load data type 2 Targa (.tga) files
// (uncompressed, unmapped RGB images)

void Image::SaveTGA(const char *filename) const {
	// must end in .tga
	const char *ext = &filename[strlen(filename)-4];
	assert(!strcmp(ext,".tga"));
	FILE *file = fopen(filename,"wb");
	// misc header information
	for (int i = 0; i < 18; i++) {
		unsigned char tmp;
		if (i == 2) WriteByte(file,2);
		else if (i == 12) WriteByte(file,width%256);
		else if (i == 13) WriteByte(file,width/256);
		else if (i == 14) WriteByte(file,height%256);
		else if (i == 15) WriteByte(file,height/256);
		else if (i == 16) WriteByte(file,24);
		else if (i == 17) WriteByte(file,32);
		else WriteByte(file,0);
	}
	// the data
	// flip y so that (0,0) is bottom left corner
	for (int y = height-1; y >= 0; y--) {
		for (int x = 0; x < width; x++) {
			Vec3f v = GetPixel(x,y);
			// note reversed order: b, g, r
			WriteByte(file,ClampColorComponent(v.b()));
			WriteByte(file,ClampColorComponent(v.g()));
			WriteByte(file,ClampColorComponent(v.r()));
		}
	}
	fclose(file);
}

Image* Image::LoadTGA(const char *filename) {
	// must end in .tga
	const char *ext = &filename[strlen(filename)-4];
	assert(!strcmp(ext,".tga"));
	FILE *file = fopen(filename,"rb");
	// misc header information
	int width = 0;
	int height = 0;
	for (int i = 0; i < 18; i++) {
		unsigned char tmp;
		tmp = ReadByte(file);
		if (i == 2) assert(tmp == 2);
		else if (i == 12) width += tmp;
		else if (i == 13) width += 256*tmp;
		else if (i == 14) height += tmp;
		else if (i == 15) height += 256*tmp;
		else if (i == 16) assert(tmp == 24);
		else if (i == 17) assert(tmp == 32);
		else assert(tmp == 0);
	}
	// the data
	Image *answer = new Image(width,height);
	// flip y so that (0,0) is bottom left corner
	for (int y = height-1; y >= 0; y--) {
		for (int x = 0; x < width; x++) {
			unsigned char r,g,b;
			// note reversed order: b, g, r
			b = ReadByte(file);
			g = ReadByte(file);
			r = ReadByte(file);
			Vec3f color(r/255.0,g/255.0,b/255.0);
			answer->SetPixel(x,y,color);
		}
	}
	fclose(file);
	return answer;
}

// ====================================================================
// ====================================================================
// Save and Load PPM image files using magic number 'P6' 
// and having one comment line

void Image::SavePPM(const char *filename) const {
	// must end in .ppm
	const char *ext = &filename[strlen(filename)-4];
	assert(!strcmp(ext,".ppm"));
	FILE *file = fopen(filename, "w");
	// misc header information
	assert(file != NULL);
	fprintf (file, "P6\n");
	fprintf (file, "# Creator: Image::SavePPM()\n");
	fprintf (file, "%d %d\n", width,height);
	fprintf (file, "255\n");
	// the data
	// flip y so that (0,0) is bottom left corner
	for (int y = height-1; y >= 0; y--) {
		for (int x=0; x<width; x++) {
			Vec3f v = GetPixel(x,y);
			fputc (ClampColorComponent(v.r()),file);
			fputc (ClampColorComponent(v.g()),file);
			fputc (ClampColorComponent(v.b()),file);
		}
	}
	fclose(file);
}

Image* Image::LoadPPM(const char *filename) {
	// must end in .ppm
	const char *ext = &filename[strlen(filename)-4];
	assert(!strcmp(ext,".ppm"));
	FILE *file = fopen(filename,"rb");
	// misc header information
	int width = 0;
	int height = 0;  
	char tmp[100];
	fgets(tmp,100,file); 
	assert (strstr(tmp,"P6"));
	fgets(tmp,100,file); 
	assert (tmp[0] == '#');
	fgets(tmp,100,file); 
	sscanf(tmp,"%d %d",&width,&height);
	fgets(tmp,100,file); 
	assert (strstr(tmp,"255"));
	// the data
	Image *answer = new Image(width,height);
	// flip y so that (0,0) is bottom left corner
	for (int y = height-1; y >= 0; y--) {
		for (int x = 0; x < width; x++) {
			unsigned char r,g,b;
			r = fgetc(file);
			g = fgetc(file);
			b = fgetc(file);
			Vec3f color(r/255.0,g/255.0,b/255.0);
			answer->SetPixel(x,y,color);
		}
	}
	fclose(file);
	return answer;
}

// ====================================================================
// ====================================================================
#endif
